Skip to content

Conversation

@kavishdevar
Copy link
Owner

@kavishdevar kavishdevar commented Sep 10, 2025

The app now supports

  • Accessibility Features
    • customizing transparency mode
    • enabling loud sound reduction
    • eq settings for transparency mode
    • eq settings for media and phone
    • hearing aid customization (Help needed from users in supported regions 🙏)
    • implement adding hearing test results and display active audiogram data (hearing loss data for 8 frequencies)
  • Multi-device connectivity
    • connect your AirPods with up to two devices simultaneously (more devices can be connected in Apple's ecosystem probably by iCloud)
    • shows popups on phone when other device takes control
    • asks the other device to show popups the same way iDevices do (will show up as an iphone because unfortunately apple's OSes don't actually pick up the names other than Mac, iPad, and iPhone specifically.
    • replicates most of the behavior for multi-device pairing such as hijack requests, reverse connection on banner tap, etc.
    • quirk: connect your apple device before connecting your non-apple device.

(Accessibility features needs writing a ATTManager because I'd love to have more direct control over the connection, WIP) done

other chores (not specific to the PR):

  • remove READ_LOCAL_ADDRESS permission
  • figure out where MODIFY_PHONE_STATE is used
  • automatically switch to using alternate head tracking packets

How?

After hours of digging, I finally found that these things require the vendorId of the Device Identification Profile to be set to Apple's.

  • On Linux, this can be easily done by editing the /etc/bluetooth/main.conf file and setting the DeviceID parameter.
  • And for Android, we again fall back to xposed, where the function that adds this DID record to SDP (Service Display Protocol is hijacked). the hooks need to be reset to find the offset for the said function (and no, i'm not going for heuristics any time soon, this is just much more reliable, one-time setup).
    • add some indication for new features/updates for existing users so that they don't need to turn to the README and/or the release notes every update

oh, and i tried android studio's code inspection and cleaned up the code a little

@kavishdevar kavishdevar self-assigned this Sep 10, 2025
@kavishdevar kavishdevar added enhancement New feature or request help wanted Extra attention is needed android Android app related issues labels Sep 10, 2025
@kavishdevar

This comment was marked as outdated.

@Leclowndu93150
Copy link

the work done here is amazing, i hope the pros 3 keep the same packet format

@kavishdevar
Copy link
Owner Author

the work done here is amazing

thanks!

i hope the pros 3 keep the same packet format

Hopefully, Apple will not go all the way to change their entire protocol that they've been using over so many years

Have you been able to try this build out, @Leclowndu93150? The accessibility fetures and stuff?

I'm waiting for a few more days just to test it out myself before merging it.

@Leclowndu93150
Copy link

Leclowndu93150 commented Sep 17, 2025

Have you been able to try this build out, @Leclowndu93150? The accessibility fetures and stuff?

not yet not yet ! i was looking for this kind of apps cuz i'm getting my first ever airpods (pros 3) on the 19th, i never had an apple product before so i'll see how it goes. but i'd be happy to help !

@Leclowndu93150
Copy link

Leclowndu93150 commented Sep 19, 2025

pfft the store is late, tomorow for sure

@kavishdevar kavishdevar closed this Oct 2, 2025
@kavishdevar kavishdevar deleted the multi-device-and-accessibility branch October 2, 2025 05:22
@kavishdevar kavishdevar restored the multi-device-and-accessibility branch October 2, 2025 05:24
@kavishdevar kavishdevar reopened this Oct 2, 2025
@randshell
Copy link

I've realised that the latest nightly is not packaged as a magisk module, so I've used the zip from v0.1.0-rc.4 and replaced the APK binary with the nightly.

For starters, there's bootloop when flashing the magisk module on my A16 device:

java.lang.IllegalStateException: Signature|privileged permissions not in privileged permission allowlist: {me.kavishdevar.librep
ods (/system/priv-app/LibrePods): android.permission.LOCAL_MAC_ADDRESS}

So I've changed the XML permission as follows which fixes it:

<?xml version="1.0" encoding="utf-8"?>
<permissions>
    <privapp-permissions package="me.kavishdevar.librepods">
        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
        <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
        <permission name="android.permission.READ_PHONE_STATE"/>
    </privapp-permissions>
</permissions>

The READ_PHONE_STATE doesn't bootloop when it's missing, but it still creates some errors in the logcat. I didn't find that it's a privileged permission, so perhaps it's missing the permission prompt at runtime, rather than editing the XML file?

Ah, I don't think that's the Media Assist feature (correct me if i'm wrong). The amplification and balance would should up under Adjustments once you enable Hearing Aid. What you're seeing is from the hearing test (I can't be bothered to be deal with accurate testing using AirPods, instead I'll just provide adding audiogram results and using it for hearing aid like Apple already provides).

I think this should be correct afaik.

From the screenshots I got from someone (I am in a region where the feature is not supported), Here's a list of things I know:

hearing test results: either from the hearing test that's taken using AirPods, or adding it manually via the Health app.
hearing aid:
hearing test results: the hearing loss data (raw dBHL values) from the hearing test. this is not customizable.
adjustments: once hearing aid is enabled, there are a few things that are customizable, like the amplification and balance (which do not alter the loss data in any way, these are different fields in the data sent), tone ambient noise etc..
if enabled, the "customize transparency mode" and "headphone accommodation" (EQ) are disabled.

I think this is also correct.

I've put up a very quickly made demo video in the README. Currently the LibrePods presents itself as "Android" which is unfortunately not used by iOS/macOS. It just checks for "Mac" or "iPad" and defaults to iPhone. I guess that iOS doesn't show the UI unless it's specifically Mac/iPad.

I've enabled the iPhone feature. Anyway, what I meant is that LibrePods doesn't show the popup when the control goes to the iPhone. The iPhone correctly shows the popup when the control goes to LibrePods and Android. This was before. Now with the nightly and with the correct permissions set by the root module, I see the popup of LibrePods. However, I'm experiencing a new issue. The switch happens Android -> iPhone, but then it doesn't come back iPhone -> Android anymore. I can still change the settings on LibrePods, e.g., enable noise cancellation, but there's no audio. It's worth noting that the system bluetooth widget shows no connected device, while LibrePods shows in-app the AirPods but does send a notification for disconnected AirPods.

Lastly, I can enable now Hearing Aid without crashing, but the settings don't work at all, for example amplification. When I switch to iPhone I can see that hearing aid was enabled, so something did happen (I couldn't tell the difference just by listening, perhaps cause it was on default amplification settings).

Recap:

  1. needed new xml privapp perms
  2. seamless transition works Android to iPhone but not the other way around
  3. Hearing Aid settings, e.g., Amplification, don't work.
  4. I experience random disconnects, perhaps related to point 2?

I haven't retested the transparency and EQs.

@randshell
Copy link

I'll attach a logcat here to help narrow down other remaining issues.

logcat_2025-10-04_15-20-08.txt


A few things to note:

  • Caused by: java.lang.SecurityException: Neither user 10050 nor current process has android.permission.MODIFY_PHONE_STATE I didn't see this one before 🤔
  • Received HEADTRACKING packet too short: for me the "Use alternate packets for headtracking" works, which I forgot to enable after reinstalling LibrePods. Perhaps in the future the app could listen for packets for a while and if it doesn't receive any data it could automatically try the alternate packets and save this setting for next time AirPods are connected? Anyway, this is beyond the scope of this MR.

@kavishdevar
Copy link
Owner Author

kavishdevar commented Oct 6, 2025

For starters, there's bootloop when flashing the magisk module on my A16 device:

I accidentally kept that READ_LOCAL_ADDRESS in the manifest, I had to remove it and use something else to get the MAC because not everyone has the app installed as a system app. (especially because I don't give the module in the release). Removing that permission. Apologies for the inconvenience!

I can still change the settings on LibrePods, e.g., enable noise cancellation, but there's no audio. It's worth noting that the system bluetooth widget shows no connected device, while LibrePods shows in-app the AirPods but does send a notification for disconnected AirPods.

I added disconnecting the audio source when other device takes over and reconnect when media starts playing. Else, when other device wants to start playing, AirPods pause the media for some reason. So when a request to lose ownership is received, the app had to disconnect A2DP. I'll add this as an option instead of forcing it.

MODIFY_PHONE_STATE permission:

Not sure at this point where that was needed, but I'll have a look and clean it up.

Hearing aid stuff:

Did you have any hearing test active? If a hearing test hasn't been sent to the AirPods, the app just defaults to zero gains for all frequency (I just copy all the audiogram data that's on the AirPods because of the reason I mentioned previously- I haven't implemented actual hearing tests, just "adjustments").

Since your iPhone did show it enabled, probably the adjustments are also updated, unless something went wrong.

I experience random disconnects

Is that when any audio is playing from Android? I have had this issue where if another device is playing audio for some time, then AirPods just disconnect the Android device, but not if it's actively playing audio or something. Don't know how to fix this, maybe it's cloud-based.

Oh, and yeah- I'll implement automatically switching to using alternate head tracking packets on this branch itself, not a major change. I haven't done it yet because that opcode isn't just for the head positioning information, so probably need to figure out another way than just length.

@randshell
Copy link

randshell commented Oct 14, 2025

Perhaps a little unrelated: I've recently bumped into a payed software for controlling AirPods at https://magicpods.app/.

I doubt that someone else took the effort to reverse engineer the protocol from scratch when you already did plenty of work, so I suspect it could be based on LibrePods. I don't have the time to check it better, so I'm sharing for you to know.

Edit. One of the public repos seems to be at https://github.com/steam3d/MagicPodsCore/.

@kavishdevar
Copy link
Owner Author

Perhaps a little unrelated: I've recently bumped into a payed software for controlling AirPods at https://magicpods.app/.

I doubt that someone else took the effort to reverse engineer the protocol from scratch when you already did plenty of work, so I suspect it could be based on LibrePods. I don't have the time to check it better, so I'm sharing for you to know.

Thanks for sharing-- I have actually shared a few things (conversational awareness, adaptive transparency, encrypted LE advertisments which I have also shared with CAPod’s developer because CAPod has a greater number of users and it was definitely an improvement for non-rooted devices) with steam3d, the developer of MagicPods. And they

Though I have written the entire documentation myself and reverse engineered the protocol myself, MagicPods had a few features before LibrePods even existed :)

@randshell
Copy link

Apologies for the inconvenience!

Not at all! 🙂

Did you have any hearing test active? If a hearing test hasn't been sent to the AirPods, the app just defaults to zero gains for all frequency (I just copy all the audiogram data that's on the AirPods because of the reason I mentioned previously- I haven't implemented actual hearing tests, just "adjustments").

Yes, I did have a hearing test done. Not sure what happened then.

Is that when any audio is playing from Android? I have had this issue where if another device is playing audio for some time, then AirPods just disconnect the Android device, but not if it's actively playing audio or something. Don't know how to fix this, maybe it's cloud-based.

I don't remember if it's also when playing. 🤔

[...] Though I have written the entire documentation myself and reverse engineered the protocol myself, MagicPods had a few features before LibrePods even existed :)

Thanks! I was curious about it.


I don't think I'll be able to test this MR anytime soon, so I hope I could be of help so far.
Thanks again for all your work!

@kavishdevar
Copy link
Owner Author

I don't think I'll be able to test this MR anytime soon, so I hope I could be of help so far.

You've been great help-- thank you very much!

I think I am done with the original goal of this PR now that I have adding hearing test results. Going to clean up the patch/hook mess and the root modules and going to merge it.

@kavishdevar kavishdevar marked this pull request as ready for review October 26, 2025 15:23
@kavishdevar kavishdevar merged commit 8eb6eba into main Oct 26, 2025
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

android Android app related issues enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants